#ifdef __cplusplus
#error "Must be compiled as C"
#endif

#ifndef NULL
#define NULL ((void*)0)
#endif

typedef void (__cdecl *_PVFV)(void);
typedef int  (__cdecl *_PIFV)(void);

extern void __cdecl __set_app_type (int at);

#define _UNKNOWN_APP    0
#define _CONSOLE_APP    1
#define _GUI_APP        2

#define _IMP___FMODE    (__p__fmode())
#define _IMP___COMMODE  (__p__commode())
#define __initenv  (*__p___initenv())

extern _PVFV *__onexitbegin;
extern _PVFV *__onexitend;
//extern int _fmode;          /* must match the definition in <stdlib.h> */
//extern int _commode;        /* must match the definition in <internal.h> */
//extern int _dowildcard;     /* passed to __getmainargs() */
#define _fmode (0)
#define _commode (0)
#define _dowildcard (0)
extern int * _IMP___FMODE;      /* exported from the CRT DLL */
extern int * _IMP___COMMODE;    /* these names are implementation-specific */
char *** __cdecl __p___initenv(void);
extern char **__initenv;

typedef struct
{
        int newmode;
} _startupinfo;
struct _EXCEPTION_POINTERS;
typedef char _TSCHAR;

void _setdefaultprecision(void);
void __cdecl __getmainargs(int *, char ***, char ***, int, _startupinfo *);
void __cdecl _setargv (void);
void __cdecl _initterm(_PVFV *, _PVFV *);
int __cdecl _XcptFilter(unsigned long, struct _EXCEPTION_POINTERS *);
unsigned long __cdecl _exception_code(void);
void *        __cdecl _exception_info(void);
__declspec(noreturn) void   __cdecl _exit(int);
__declspec(noreturn) void   __cdecl exit(int);
extern int main(int, char **, char **);

long __cdecl _ftol2_sse(double x);

#define GetExceptionCode            _exception_code
#define GetExceptionInformation     (struct _EXCEPTION_POINTERS *)_exception_info

#define _CRTALLOC(x) __declspec(allocate(x))

// This is for VC++ 10 // TODO: Adapt to different versions
#pragma section(".CRT$XCA",long,read)
#pragma section(".CRT$XCZ",long,read)
#pragma section(".CRT$XIA",long,read)
#pragma section(".CRT$XIZ",long,read)

extern _CRTALLOC(".CRT$XIA") _PIFV __xi_a[];
extern _CRTALLOC(".CRT$XIZ") _PIFV __xi_z[];    /* C initializers */
extern _CRTALLOC(".CRT$XCA") _PVFV __xc_a[];
extern _CRTALLOC(".CRT$XCZ") _PVFV __xc_z[];    /* C++ initializers */

#ifndef _USRDLL

void __stdcall mainCRTStartup(void)
{
	_startupinfo startinfo;
	int argc;
    _TSCHAR **argv;
    _TSCHAR **envp;

	// TODO: SEH without the SEH construct

	{
		__set_app_type(_CONSOLE_APP);

		__onexitbegin = __onexitend = (_PVFV *)(-1);

		*_IMP___FMODE = _fmode;
        *_IMP___COMMODE = _commode;

		//_setdefaultprecision();
		
		// This is for VC++ 10 // TODO: Adapt to different versions
		_initterm((_PVFV *)(void *)__xi_a, (_PVFV *)(void *)__xi_z);

		__getmainargs(&argc, &argv, &envp, _dowildcard, &startinfo);

		__initenv = envp;
		exit(main(argc, argv, envp));
	}
}

#else // _USRDLL

#define DLL_PROCESS_ATTACH   1  
#define DLL_THREAD_ATTACH    2 
#define DLL_THREAD_DETACH    3
#define DLL_PROCESS_DETACH   0

typedef unsigned long size_t; // !ARCH x64
typedef int (__cdecl * _onexit_t)(void);

void*  __cdecl malloc(size_t);
void __cdecl free(void*);
_PVFV __dllonexit (_PVFV, _PVFV**, _PVFV**);

static void init_crt(void* hDll, void* lpReserved)
{
	__onexitbegin = (_PVFV*)malloc(32 * sizeof(_PVFV));
	*(__onexitbegin) = (_PVFV)NULL;
	__onexitend = __onexitbegin;

	// This is for VC++ 10 // TODO: Adapt to different versions
	_initterm((_PVFV *)(void *)__xi_a, (_PVFV *)(void *)__xi_z);
}

static void __cdecl deinit_crt()
{
	if(__onexitbegin)
	{
        _PVFV* pfend = __onexitend;

        while(--pfend >= __onexitbegin)
            if(*pfend != NULL)
                (**pfend)();

        free(__onexitbegin);
        __onexitbegin = NULL;
    }
}

extern int __stdcall DllMain(void*, unsigned int, void*);

int __stdcall _msvcrt_entry_dll(void* hDll, unsigned int dwReason, void* lpReserved)
{
	int ret;
	switch(dwReason)
	{
		case DLL_PROCESS_DETACH:
		{
			deinit_crt();
			break;
		}

		case DLL_PROCESS_ATTACH:
		{
			init_crt(hDll, lpReserved);
			break;
		}
	}

	//ret = DllMain(hDll, dwReason, lpReserved);
	ret = 1;

	if((dwReason == DLL_PROCESS_ATTACH && !ret)
	 || dwReason == DLL_PROCESS_DETACH)
    {
		deinit_crt();
	}

	return ret;
}

#endif


//#pragma comment(linker, "/include:__ftol2_sse")

__declspec(naked) long __cdecl _ftol2_sse(double x)
{
	__asm
	{
		fstp        qword ptr [esp-8]
		cvttsd2si   eax, mmword ptr [esp-8]
		ret
	}
}

__declspec(naked) long __cdecl _ftol2(double x)
{
	__asm
	{
		fstp        qword ptr [esp-8]
		cvttsd2si   eax, mmword ptr [esp-8]
		ret
	}
}

//#pragma comment(linker, "/include:__aulldvrm")

__declspec(naked) unsigned __int64 __cdecl _aulldvrm(unsigned __int64 a, unsigned __int64 b)
{
	__asm
	{
		push    esi

; Set up the local stack and save the index registers.  When this is done
; the stack frame will look as follows (assuming that the expression a/b will
; generate a call to aulldvrm(a, b)):
;
;               -----------------
;               |               |
;               |---------------|
;               |               |
;               |--divisor (b)--|
;               |               |
;               |---------------|
;               |               |
;               |--dividend (a)-|
;               |               |
;               |---------------|
;               | return addr** |
;               |---------------|
;       ESP---->|      ESI      |
;               -----------------
;


;
; Now do the divide.  First look to see if the divisor is less than 4194304K.
; If so, then we can use a simple algorithm with word divides, otherwise
; things get a little more complex.
;

		mov     eax,[esp + 12] ; check to see if divisor < 4194304K
		or      eax,eax
		jnz     short L1        ; nope, gotta do this the hard way
		mov     ecx,[esp + 16] ; load divisor
		mov     eax,[esp + 12] ; load high word of dividend
		xor     edx,edx
		div     ecx             ; get high order bits of quotient
		mov     ebx,eax         ; save high bits of quotient
		mov     eax,[esp + 8] ; edx:eax <- remainder:lo word of dividend
		div     ecx             ; get low order bits of quotient
		mov     esi,eax         ; ebx:esi <- quotient

;
; Now we need to do a multiply so that we can compute the remainder.
;
        mov     eax,ebx         ; set up high word of quotient
        mul     dword ptr [esp + 16] ; HIWORD(QUOT) * DVSR
        mov     ecx,eax         ; save the result in ecx
        mov     eax,esi         ; set up low word of quotient
        mul     dword ptr [esp + 16] ; LOWORD(QUOT) * DVSR
        add     edx,ecx         ; EDX:EAX = QUOT * DVSR
        jmp     short L2        ; complete remainder calculation

;
; Here we do it the hard way.  Remember, eax contains DVSRHI
;

L1:
        mov     ecx,eax         ; ecx:ebx <- divisor
        mov     ebx,[esp + 16]
        mov     edx,[esp + 12] ; edx:eax <- dividend
        mov     eax,[esp + 8]
L3:
        shr     ecx,1           ; shift divisor right one bit; hi bit <- 0
        rcr     ebx,1
        shr     edx,1           ; shift dividend right one bit; hi bit <- 0
        rcr     eax,1
        or      ecx,ecx
        jnz     short L3        ; loop until divisor < 4194304K
        div     ebx             ; now divide, ignore remainder
        mov     esi,eax         ; save quotient

;
; We may be off by one, so to check, we will multiply the quotient
; by the divisor and check the result against the orignal dividend
; Note that we must also check for overflow, which can occur if the
; dividend is close to 2**64 and the quotient is off by 1.
;

        mul     dword ptr [esp + 20] ; QUOT * HIWORD(DVSR)
        mov     ecx,eax
        mov     eax,[esp + 16]
        mul     esi             ; QUOT * LOWORD(DVSR)
        add     edx,ecx         ; EDX:EAX = QUOT * DVSR
        jc      short L4        ; carry means Quotient is off by 1

;
; do long compare here between original dividend and the result of the
; multiply in edx:eax.  If original is larger or equal, we are ok, otherwise
; subtract one (1) from the quotient.
;

        cmp     edx,[esp + 12] ; compare hi words of result and original
        ja      short L4        ; if result > original, do subtract
        jb      short L5        ; if result < original, we are ok
        cmp     eax,[esp + 8] ; hi words are equal, compare lo words
        jbe     short L5        ; if less or equal we are ok, else subtract
L4:
        dec     esi             ; subtract 1 from quotient
        sub     eax,[esp + 16] ; subtract divisor from result
        sbb     edx,[esp + 20]
L5:
        xor     ebx,ebx         ; ebx:esi <- quotient

L2:
;
; Calculate remainder by subtracting the result from the original dividend.
; Since the result is already in a register, we will do the subtract in the
; opposite direction and negate the result.
;

        sub     eax,[esp + 8] ; subtract dividend from result
        sbb     edx,[esp + 12]
        neg     edx             ; otherwise, negate the result
        neg     eax
        sbb     edx,0

;
; Now we need to get the quotient into edx:eax and the remainder into ebx:ecx.
;
        mov     ecx,edx
        mov     edx,ebx
        mov     ebx,ecx
        mov     ecx,eax
        mov     eax,esi
;
; Just the cleanup left to do.  edx:eax contains the quotient.
; Restore the saved registers and return.
;

        pop     esi

        ret     16
	}
}